#include "../include/argparse.hpp"


// ArgParser declaration
void arg::ArgParser::addNoneParamArgument(bool isRequired, std::string name)
{
	_arguments[name] = std::make_shared<NoneParamArgument>(isRequired, name);
	_arguments[name]->setType(NONE_PARAM_ARG);	
}
void arg::ArgParser::addBoolArgument(bool isRequired, std::string name, std::string truePhrase, std::string falsePhrase)
{
	_arguments[name] = std::make_shared<BoolArgument>(isRequired, name, truePhrase, falsePhrase);
	_arguments[name]->setType(BOOL_PARAM_ARG);
}
void arg::ArgParser::addStringArgument(bool isRequired, std::string name)
{ 
	_arguments[name] = std::make_shared<StringArgument>(isRequired, name); 
	_arguments[name]->setType(STR_PARAM_ARG);
}

void arg::ArgParser::parse(int argc, char *argv[])
{
	std::vector<std::string> requiredArguments = getRequiredArguments();
	unsigned int checkedRequiredArgsCount = 0;


	for (int i = 1; i < argc; i++) {
		if (_arguments.find(argv[i]) != _arguments.end()) {
			if (_arguments[argv[i]]->isRequired()) { checkedRequiredArgsCount++; }

			switch (_arguments[argv[i]]->getType()) {
			case NONE_PARAM_ARG:
				_arguments[argv[i]]->set();	
				break;
			
			case BOOL_PARAM_ARG:
				if (i + 1 > argc-1) { soulc::raiseError(soulc::COMPILER_NO_PHRASE_FOR_BOOL_ARG_ERR, argv[i]); }

				_arguments[argv[i]]->set();
				std::dynamic_pointer_cast<BoolArgument>(_arguments[argv[i]])->setBoolValue(argv[++i]);
				
				break;

			case STR_PARAM_ARG:
				if (i + 1 > argc-1) { soulc::raiseError(soulc::COMPILER_NO_STR_FOR_STR_ARG_ERR, argv[i]); }
			
				_arguments[argv[i]]->set();
				std::dynamic_pointer_cast<StringArgument>(_arguments[argv[i]])->setText(argv[++i]);

				break;
			}
		} else {
			soulc::raiseError(soulc::COMPILER_UNKNOWN_ARG_ERR, argv[i]);
		}

	}

	if (checkedRequiredArgsCount < requiredArguments.size()) {
		soulc::raiseError(soulc::COMPILER_NO_ARGS_ERR, GET_HELP_STR);
	}
}

std::shared_ptr<arg::NoneParamArgument> arg::ArgParser::getArgument(std::string name)
{ return _arguments[name]; }

std::vector<std::string> arg::ArgParser::getRequiredArguments()
{
	std::vector<std::string> requiredArguments;

	for (auto arg : _arguments) {
		if (arg.second->isRequired()) {
			requiredArguments.push_back(arg.first);
		}
	}

	return requiredArguments;
}


// NoneParamArgument declaration
arg::NoneParamArgument::NoneParamArgument(bool isRequired, std::string name) : _type(NONE_PARAM_ARG), _name(name), _isSet(false), _isRequired(isRequired),
																				_help("")
{  }
arg::NoneParamArgument::NoneParamArgument(bool isRequired, std::string name, std::string help) : _type(NONE_PARAM_ARG), _name(name), _isSet(false), 
																			_isRequired(isRequired),
																			_help(help)
{  }

arg::NoneParamArgument& arg::NoneParamArgument::getInstance()
{ return *this; }

arg::ArgumentType arg::NoneParamArgument::getType()
{ return _type; }
void arg::NoneParamArgument::setType(arg::ArgumentType type)
{ _type = type; }

std::string arg::NoneParamArgument::getName()
{ return _name; }

bool arg::NoneParamArgument::isSet()
{ return _isSet; }
void arg::NoneParamArgument::set()
{ _isSet = true; }

bool arg::NoneParamArgument::isRequired()
{ return _isRequired; }

std::string arg::NoneParamArgument::getHelp()
{ return _help; }
void arg::NoneParamArgument::setHelp(std::string help)
{ _help = help; }


// BoolArgument declaration
arg::BoolArgument::BoolArgument(bool isRequired, std::string name, std::string truePhrase, std::string falsePhrase)
																				: NoneParamArgument(isRequired, name), 
																				_truePhrase(truePhrase),
																				_falsePhrase(falsePhrase)
{  }
arg::BoolArgument::BoolArgument(bool isRequired, std::string name, std::string truePhrase, std::string falsePhrase, std::string help)
																				: NoneParamArgument(isRequired, name, help),
																				_truePhrase(truePhrase),
																				_falsePhrase(falsePhrase)
{  }

bool arg::BoolArgument::getBoolValue()
{ return _boolValue; }
void arg::BoolArgument::setBoolValue(std::string value)
{
	if (value == _truePhrase) {	_boolValue = true; }
	else if (value == _falsePhrase) { _boolValue = false; }
	else { soulc::raiseError(soulc::COMPILER_NO_PHRASE_FOR_BOOL_ARG_ERR, std::string("[") + getName() + "] the pahrase is neither true of false"); }
}


// StringArgument declaration
arg::StringArgument::StringArgument(bool isRequired, std::string name) : NoneParamArgument(isRequired, name)
{  }
arg::StringArgument::StringArgument(bool isRequired, std::string name, std::string help) : NoneParamArgument(isRequired, name, help)
{  }

std::string arg::StringArgument::getText()
{ return _text; }
void arg::StringArgument::setText(std::string text)
{ _text = text; }
